package zhongbo.javaweb.instagram.schedule;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import zhongbo.javaweb.instagram.model.Config;
import zhongbo.javaweb.instagram.model.User;
import zhongbo.javaweb.instagram.service.model.ConfigService;
import zhongbo.javaweb.instagram.service.model.UserService;
import zhongbo.javaweb.instagram.util.DateFormatUtil;
import zhongbo.javaweb.instagram.util.HttpClientUtil;
import zhongbo.javaweb.instagram.util.StrUtil;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static zhongbo.javaweb.instagram.util.PerformanceUtil.countEndStr;
import static zhongbo.javaweb.instagram.util.PerformanceUtil.countStart;

/**
 * 说明：
 *
 * @author zhongboWork
 * @version 1.0
 * @since 2021-12-27 16:17
 */
@Component
@Slf4j
@Data
public class UserCheckSchedule {

    @Autowired
    private UserService userService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private ProxyCheckSchedule proxyCheckSchedule;
    private List<User> userList = new ArrayList<>();

    private List<String> availableUserList = new ArrayList<>();

    private List<String> banedUserList = new ArrayList<>();

    @Value("${user.check.enabled}")
    private boolean enabled = true;

    @Value("${user.check.url}")
    private String userCheckUrl = "https://api-ipv4.ip.sb/ip";
    @Value("${user.check.timeOutMs}")
    private int userCheckTimeOutMs = 10000;
    @Value("${user.check.intervalMs}")
    private long userCheckIntervalMs = 30000;
    @Value("${user.check.unBanIntervalMs}")
    private long userUnBanIntervalMs = TimeUnit.DAYS.toMillis(2);

    private String availableUser;

    private long lastCheckTime;
    private long lastUnBanTime;

    @PostConstruct
    public void initUser() {
        this.userList = userService.findAll();
    }

    //@Scheduled(fixedDelay = 1000)
    public void checkUserList() {
        if (System.currentTimeMillis() - lastCheckTime > userCheckIntervalMs) {
            lastCheckTime = System.currentTimeMillis();
            if (!enabled) {
                return;
            }
            log.info("======== 用户检查开始... 检查间隔: {}", DateFormatUtil.formatMs(userCheckIntervalMs));
            countStart();
            refreshUser();
            if (this.userList.size() > 0) {
                for (User user : this.userList) {
                    if (user.isEnabled()) {
                        doCheckUser(user);
                        userService.addOrUpdate(user);
                    }
                }
                updateAvailableUser();
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("未配置用户列表");
                }
            }
            lastCheckTime = System.currentTimeMillis();
            String countEndStr = countEndStr();
            log.info("用户检查完毕, 有效用户: {}; 当前用户: {}; 耗时 {}", availableUserList, availableUser, countEndStr);
        }

    }

    private void refreshUser() {
        userList = userService.findAll();
    }

    private void updateAvailableUser() {
        if (availableUserList.isEmpty()) {
            availableUser = null;
        } else {
            if (availableUserList.contains(availableUser)) {
                log.info("当前用户: {} (未更换)", availableUser);
                return;
            }
            int next = availableUserList.indexOf(availableUser) + 1;
            if (next >= availableUserList.size()) {
                next = 0;
            }
            String nextUser = availableUserList.get(next);
            if (!nextUser.equals(availableUser)) {
                log.info("当前用户: {}, 上个用户: {}", nextUser, availableUser);
                availableUser = nextUser;
            } else {
                log.info("当前用户: {}", availableUser);
            }
        }
    }

    private void doCheckUser(User user) {
        HttpGet httpGet = null;
        String cookies = null;
        try {
            cookies = user.getCookies();
            if (StringUtils.isEmpty(cookies)) {
                user.setAvailable(false);
                log.warn("无效的用户信息,cookies空! {}", user);
                return;
            }
            if (checkIsBan(cookies)) {
                user.setBaned(true);
                user.setBanedTime(System.currentTimeMillis());
                return;
            } else {
                user.setBaned(false);
                user.setBanedTime(0);
            }
            String availableProxy = proxyCheckSchedule.getAvailableProxy();
            if (StringUtils.isEmpty(availableProxy)) {
                log.error("检测用户失败! 无可用代理.");
                return;
            }
            // HttpClient 用户检测
            HttpHost userHost = HttpHost.create(availableProxy);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setProxy(userHost)
                    .setConnectTimeout(userCheckTimeOutMs)
                    .setRedirectsEnabled(false)
                    .build();
            CloseableHttpClient httpClient = HttpClientUtil.chooseInsClient();
            Config config = configService.get();
            String defaultUserAgent = config.getDefaultUserAgent();
            String proxyCheckUrl = config.getProxyCheckUrl();
            if (StringUtils.isEmpty(proxyCheckUrl)) {
                proxyCheckUrl = userCheckUrl;
            }
            httpGet = new HttpGet(proxyCheckUrl);
            httpGet.setHeader(HTTP.USER_AGENT, defaultUserAgent);
//            cookies = HttpUtil.updateMid(cookies);
            httpGet.setHeader(HttpHeaders.COOKIE, cookies);
            httpGet.setConfig(requestConfig);
            CloseableHttpResponse execute = httpClient.execute(httpGet);
            int statusCode = execute.getStatusLine().getStatusCode();
            int successCode = 200;
            if (statusCode == successCode) {
                String body = EntityUtils.toString(execute.getEntity());
                // http://192.168.2.126:8118 body: 101.32.33.8 20211215
                boolean statusSuccess = execute.getStatusLine().getStatusCode() == 200;
                log.info("检测用户成功! {} 用户响应 statusCode: {}, success: {}, body: {}", cookies, statusCode, statusSuccess, StrUtil.subStr(body, 0, 100));
                if (statusSuccess) {
                    user.setAvailable(true);
                    if (!availableUserList.contains(cookies)) {
                        availableUserList.add(cookies);
                    }
                    return;
                }
            } else {
                log.error("检测用户失败, 返回状态码错误! statusCode: {}", statusCode);
            }
        } catch (IOException e) {
            log.error("检测用户失败, 读取信息失败! httpGet: {} error: {}", httpGet, e);
        } catch (IllegalArgumentException e) {
            log.error("检测用户失败, 读取信息失败! cookies: {} error: {}", cookies, e);
        }
        user.setAvailable(false);
        availableUserList.remove(cookies);
    }

    private boolean checkIsBan(String cookies) {
        long banPast = System.currentTimeMillis() - lastUnBanTime;
        if (banPast > userUnBanIntervalMs) {
            log.info("到达解封周期{}, 解 ban 所有用户 {}", DateFormatUtil.formatMs(userUnBanIntervalMs), banedUserList);
            banedUserList.clear();
        }
        if (banedUserList.contains(cookies)) {
            if (log.isDebugEnabled()) {
                log.debug("用户: {} 已被封", cookies);
            }
            availableUserList.remove(cookies);
            return true;
        }
        return false;
    }

    public void banUser(String cookies) {
        if (!StringUtils.isEmpty(cookies)) {
            banedUserList.add(cookies);
        }
    }

}
